home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 015 / page2.arc / PAGE2.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-06-04  |  45.7 KB  |  917 lines

  1. Program Page2;
  2.  
  3. (*---------------------------------------------------------------------------*)
  4. (*                                                                           *)
  5. (*                           PAGE2  Version 1.0                              *)
  6. (*                                                                           *)
  7. (*         A generic program to print text on both sides of the paper        *)
  8. (*                                                                           *)
  9. (*                   by: R. P. Byrne      June 4, 1988                       *)
  10. (*                                                                           *)
  11. (*---------------------------------------------------------------------------*)
  12. (*                                                                           *)
  13. (*   I have placed this program and it's source code into the public         *)
  14. (*   domain in the hope that it may prove useful to someone other than       *)
  15. (*   myself.                                                                 *)
  16. (*                                                                           *)
  17. (*   Please feel free to distribute this program by any means available. I   *)
  18. (*   only ask, as a courtesy, that the program not be distributed without    *)
  19. (*   the inclusion of the source code and the accompanying documentation     *)
  20. (*   file.                                                                   *)
  21. (*                                                                           *)
  22. (*   Since this program is truly public domain software, if you paid         *)
  23. (*   someone more than $5.00 to receive it, you've probably been ripped      *)
  24. (*   off.                                                                    *)
  25. (*                                                                           *)
  26. (*                                                        rpb                *)
  27. (*                                                       6/4/88              *)
  28. (*                                                                           *)
  29. (*---------------------------------------------------------------------------*)
  30. (*                                                                           *)
  31. (*  Usage:   Page2 filename [options] [output filename]                      *)
  32. (*                                                                           *)
  33. (*      Allowable options are:                                               *)
  34. (*                                                                           *)
  35. (*      /W#      -  Specify the width in characters (#) of your paper        *)
  36. (*                                                                           *)
  37. (*      /L#      -  Specify the length in lines (#) of your paper            *)
  38. (*                                                                           *)
  39. (*      /M#      -  Set a "Gutter" margin of # characters for every page. A  *)
  40. (*                  gutter margin (sometimes called a "binding" margin) is   *)
  41. (*                  on that prints on the left edge of odd numbered pages    *)
  42. (*                  and on the right edge of even numbered pages.            *)
  43. (*                                                                           *)
  44. (*      /F       -  Toggle the printing of formfeed characters between pages *)
  45. (*                                                                           *)
  46. (*      /E       -  Toggle which pages get printed first (even or odd)       *)
  47. (*                                                                           *)
  48. (*      /N        -  Toggle the printing of the filename in a running header *)
  49. (*                                                                           *)
  50. (*      /P       -  Toggle the printing of page numbers in a running header  *)
  51. (*                                                                           *)
  52. (*      /D       -  Toggle the printing of the current date in a running     *)
  53. (*                  header                                                   *)
  54. (*                                                                           *)
  55. (*     Each option must be specified separately and must begin with a '/'.   *)
  56. (*     Options may be specified anywhere on the command line.                *)
  57. (*                                                                           *)
  58. (*     You may override the selection of the DOS StdPrn device for output    *)
  59. (*     by specifying a second filename on the command line.  The program     *)
  60. (*     takes the first argument not preceded by a forward slash as the       *)
  61. (*     file to be printed.  Any other such parameter encountered is assumed  *)
  62. (*     to be an output file specification.  All printer output is directed   *)
  63. (*     to this file if specified.                                            *)
  64. (*                                                                           *)
  65. (*     The program automatically handles Tab expansion, Carriage Returns     *)
  66. (*     with no corresponding Line Feed, Line Feeds with no corresponding     *)
  67. (*     Carriage Return, embedded Backspace and Form Feed characters.         *)
  68. (*                                                                           *)
  69. (*     Input lines longer than the available paper width will be truncated.  *)
  70. (*                                                                           *)
  71. (*---------------------------------------------------------------------------*)
  72.  
  73. Uses  Crt,
  74.       Dos,
  75.       StrProcs;
  76.  
  77. Type
  78.    IntPtr   = ^LongInt;          { Pointer type for PageTable array          }
  79.    TableRec = Record
  80.                  PageOfs  : LongInt;
  81.                  PageSize : Word;
  82.               end;
  83.  
  84. Const
  85.    BS        = #008;
  86.    TAB       = #009;
  87.    LF        = #010;             { Some ASCII character values               }
  88.    FF        = #012;
  89.    CR        = #013;
  90.    SPACE     = #032;
  91.  
  92.    BufSize   = 8192;
  93.    PageLimit = 1000;             { Arbitrary program limit on document size  }
  94.  
  95. Const  { Structured }
  96.  
  97.    PatchArea : String[16] = 'Patch Area Start';    { Marker for CFG program  }
  98.    CPL       : Word    = 80;     { Default Characters per Line setting       }
  99.    LPP       : Word    = 66;     { Default Lines per Page setting            }
  100.    BindMar   : Word    = 0;      { Default gutter margin setting             }
  101.    UseFF     : Boolean = FALSE;  { No Formfeeds                              }
  102.    OddFirst  : Boolean = TRUE;   { Print odd pages first                     }
  103.    PrintName : Boolean = FALSE;  { No filename in page headers               }
  104.    PrintDate : Boolean = FALSE;  { No current date in page headers           }
  105.    PrintPgNo : Boolean = FALSE;  { No page number in page headers            }
  106.    EndPatch  : String[14] = 'Patch Area End';      { Marker for CFG program  }
  107.  
  108.    Handle    : Word = 4;         { handle of output file/device (4 = stdprn) }
  109.  
  110. Var
  111.    InFile     : File;            { The file variable for the input           }
  112.    InFileName : String;          { The name of the file to be printed        }
  113.    InFileSize : LongInt;         { The size of the file to be printed (bytes)}
  114.    EOF_InFile : Boolean;         { End of File indicator                     }
  115.    InBuf      : Array[1..BufSize] of Char;  { Input buffer space             }
  116.    InBufIdx   : Word;            { An index for the input buffer             }
  117.    BytesIn    : Word;      { Count of bytes transferred at last read request }
  118.  
  119.    OutFileName   : String;                 { optional output file override   }
  120.    OutFile       : File;                   { untyped file var for output     }
  121.    OutFileHandle : Word Absolute OutFile;  { handle of output file           }
  122.  
  123.    TurboInt24  : Pointer;        { storage for Turbo Pascal's Int24 addr.    }
  124.  
  125.    PageTable   : Array[1..PageLimit] of ^TableRec;  { Page description table }
  126.  
  127.    PageCount   : Word;
  128.    SheetCount  : Word;
  129.    LineCount   : LongInt;        { Statistics on what gets printed           }
  130.  
  131.    CurrentPage : Word;
  132.    CurrentLine : Word;
  133.    CurrentChar : Word;           { Status trackers                           }
  134.  
  135.    CurrentMar  : Word;           { Page specific margin setting now in use   }
  136.    CurrentCPL  : Word;           { Page specific CPL setting now in use      }
  137.  
  138.    PrintHdr    : Boolean;        { running header print flag                 }
  139.    PageHdr     : String;         { Space to store running page header        }
  140.    HdrName     : String[12];     { Space to store filename for page hdr      }
  141.    HdrDate     : String[18];     { Space to store current date for page hdr  }
  142.    HdrPage     : String[5];      { Space to store page no. text for pg. hdr  }
  143.  
  144.    MsgX, MsgY  : Word;           { Cursor position placeholders              }
  145.  
  146. { --------------------------------------------------------------------------- }
  147. {  Houskeeping routines ...                                                   }
  148. { --------------------------------------------------------------------------- }
  149.  
  150. Procedure Abort(ErrMsg : String);
  151. Begin
  152.    If ErrMsg <> '' then begin
  153.       Writeln;
  154.       Writeln(ErrMsg);
  155.       Writeln;
  156.    end {if};
  157.    Halt(255);
  158. End {Abort};
  159.  
  160. { --------------------------------------------------------------------------- }
  161.  
  162. Procedure Syntax;
  163. Begin
  164.    TextColor(LightCyan);
  165.    TextBackground(Black);
  166.    ClrScr;
  167.    Writeln;
  168.    Writeln('  Usage:  Page2 filename [options]');
  169.    Writeln;
  170.    Writeln('  Allowable options are:');
  171.    Writeln;
  172.    Writeln('  /W#  -  Set page Width to # characters [default = ', IntStr(CPL, 0), ']');
  173.    Writeln('  /L#  -  Set page Length to # lines [default = ', IntStr(LPP, 0), ']');
  174.    Writeln('  /M#  -  Set a gutter Margin of # characters [default = ', IntStr(BindMar,0), ']');
  175.    Writeln;
  176.    Writeln('  The following options  ay be toggled from one setting to another:');
  177.    Writeln;
  178.    Writeln('  /F   -  Toggle the printing of formfeed characters at the end of each page');
  179.    Write('          [default is ');
  180.    If UseFF then
  181.       Writeln('ON]')
  182.    Else
  183.       Writeln('OFF]');
  184.    Writeln('  /E   -  Toggle which pages get printed first (ie. even or odd)');
  185.    Write('          [default is to print the ');
  186.    If OddFirst then
  187.       Write('odd')
  188.    else
  189.       Write('even');
  190.    Writeln(' pages first]');
  191.    Writeln;
  192.    Writeln('  The following option toggles constitute a running page header definition.');
  193.    Writeln('  If all options are toggled OFF, no page headers will be printed.');
  194.    Writeln;
  195.    Write('  /N   -  Include file Name in headers toggle [default = ');
  196.    If PrintName then
  197.       Writeln('ON]')
  198.    else
  199.       Writeln('OFF]');
  200.    Write('  /D   -  Include current Date in headers toggle [default = ');
  201.    If PrintDate then
  202.       Writeln('ON]')
  203.    else
  204.       Writeln('OFF]');
  205.    Write('  /P   -  Include Page numbers in headers toggle [default = ');
  206.    If PrintPgNo then
  207.       Writeln('ON]')
  208.    else
  209.       Writeln('OFF]');
  210.    Abort('');
  211. End {Syntax};
  212.  
  213. { --------------------------------------------------------------------------- }
  214.  
  215. Procedure Parse_Command_Line;
  216. Var
  217.    CmdLine   : ^String;
  218.    Token     : String;
  219.    I         : Word;
  220.    Code      : Integer;
  221. Begin
  222.    If ParamCount < 1 then
  223.       Syntax;
  224.    CmdLine := Ptr(PrefixSeg, $80);   { set up pointer to command line in psp }
  225.    InFileName  := '';
  226.    OutFileName := '';
  227.    While CmdLine^ <> '' do begin
  228.       Token := GetToken(CmdLine^, TRUE);
  229.       If (Token[1] in ['-', '/']) then begin       { Option switch character }
  230.          Delete(Token, 1, 1);                   { Strip the switch character }
  231.          If Token <> '' then                       { Anything left?          }
  232.             Case Token[1] of
  233.                'W' : Begin                           { Page width override     }
  234.                         Delete(Token, 1, 1);
  235.                         If Token <> '' then begin
  236.                            Val(Token, CPL, Code);
  237.                            If Code <> 0 then
  238.                               Abort('Invalid page width entered!');
  239.                         end {if};
  240.                      end {'W'};
  241.                'L' : Begin                           { Page length override    }
  242.                         Delete(Token, 1, 1);
  243.                         If Token <> '' then begin
  244.                            Val(Token, LPP, Code);
  245.                            If Code <> 0 then
  246.                               Abort('Invalid page length entered!');
  247.                         end {if};
  248.                      end {'L'};
  249.                'M' : Begin                           { Gutter margin override  }
  250.                         Delete(Token, 1, 1);
  251.                         Val(Token, BindMar, Code);
  252.                         If Code <> 0 then
  253.                            Abort('Invalid binding margin entered!');
  254.                      end;
  255.                'E' : OddFirst  := Not OddFirst;      { First Pass toggle       }
  256.                'F' : UseFF     := Not UseFF;         { Formfeed toggle         }
  257.                'N' : PrintName := Not PrintName;     { Header content override }
  258.                'D' : PrintDate := Not PrintDate;
  259.                'P' : PrintPgNo := Not PrintPgNo;
  260.             end {case};
  261.       end {if}
  262.       else
  263.          If InFileName = '' then                     { 1st name is file to prt }
  264.             InFileName := Token
  265.          else
  266.             If OutFileName = '' then begin           { 2nd name is printfile   }
  267.                OutFileName := Token;
  268.             end {then}
  269.             else
  270.                Abort('Unexpected command line argument encountered!');
  271.    end {while};
  272.  
  273. End {Parse_Command_Line};
  274.  
  275. { --------------------------------------------------------------------------- }
  276.  
  277. Procedure Prog_Init;
  278. Var
  279.    SearchBuf : SearchRec;         { Buffer for Find First/Next DOS functions }
  280.    Attribute : Word;
  281.    Regs      : Registers;
  282. Begin
  283.    With Regs do begin
  284.       AH := $35;
  285.       AL := $24;
  286.       MsDos(Regs);
  287.       TurboInt24 := Ptr(ES, BX);
  288.    end {with};
  289.  
  290.    Attribute := ReadOnly or Hidden or SysFile or Archive;
  291.    FindFirst(InFileName, Attribute, SearchBuf);
  292.    If DosError <> 0 then
  293.       Abort('Could not locate file [' + InFileName + ']!');
  294.    Assign(InFile, InFileName);
  295.  
  296.    If OutFileName <> '' then begin
  297.       Assign(OutFile, OutFileName);
  298.       Rewrite(OutFile, 1);
  299.       Handle := OutFileHandle;
  300.    end {if};
  301.  
  302.    PrintHdr := (PrintName or PrintDate or PrintPgNo);
  303.    If PrintName then
  304.       HdrName := NameOnly(InFileName)
  305.    else
  306.       HdrName := '';
  307.    If PrintDate then
  308.       HdrDate := DateStr
  309.    else
  310.       HdrDate := '';
  311.    If PrintPgNo then
  312.       HdrPage := 'Page '
  313.    else
  314.       HdrPage := '';
  315.  
  316.    TextColor(Yellow);
  317.    TextBackground(Black);
  318.    ClrScr;
  319.    Writeln;
  320.    Writeln('Page2 - Generic 2-sided print utility - by R. P. Byrne 5/24/88');
  321.    Writeln;
  322.    TextColor(LightGreen);
  323.    Writeln('Options in effect are');
  324.    Writeln;
  325.    Writeln('   Page width     : ', CPL);
  326.    Writeln('   Page length    : ', LPP);
  327.    Writeln('   Gutter margin  : ', BindMar);
  328.    Write('   Formfeeds      : ');
  329.    If UseFF then
  330.       Writeln('On')
  331.    else
  332.       Writeln('Off');
  333.    Write('   First printing : ');
  334.    If OddFirst then
  335.       Writeln('Odd pages')
  336.    else
  337.       Writeln('Even pages');
  338.    Write('   Page headers   : ');
  339.    If PrintHdr then begin
  340.       If PrintName then
  341.          Write('Filename  ');
  342.       If PrintDate then
  343.          Write('Date  ');
  344.       If PrintPgNo then
  345.          Write('Page Numbers  ');
  346.       Writeln;
  347.    end {then}
  348.    else
  349.       Writeln('None');
  350.  
  351.    TextColor(Yellow);
  352.    TextBackground(Black);
  353.  
  354. End {Prog_Init};
  355.  
  356. { --------------------------------------------------------------------------- }
  357. {  Ok, here are the routines that do the work ...                             }
  358. { --------------------------------------------------------------------------- }
  359.  
  360. Procedure Read_Input;
  361. Begin
  362.    EOF_InFile := FALSE;
  363.    BlockRead(InFile, InBuf, SizeOf(InBuf), BytesIn);
  364.    InBufIdx := 0;
  365.    If BytesIn = 0 then
  366.       EOF_InFile := TRUE;
  367. End {Read_Input};
  368.  
  369. { --------------------------------------------------------------------------- }
  370.  
  371. Procedure NewPage;
  372. Begin
  373.    Inc(CurrentPage);
  374.    If CurrentPage > PageLimit then
  375.       Abort('Too many pages to print.  Maximum is ' + IntStr(PageLimit, 0) + '!');
  376.  
  377.    LineCount := LineCount + CurrentLine;
  378.    CurrentLine := 0;
  379.    CurrentChar := 0;
  380.  
  381.    If PrintHdr then
  382.       Inc(CurrentLine, 2);
  383.  
  384.    If (MemAvail < (SizeOf(IntPtr) + 128)) then
  385.       Abort('Insufficient memory to process file!');
  386.    New(PageTable[CurrentPage]);
  387.  
  388.    PageTable[CurrentPage]^.PageOfs  := FilePos(InFile) - BytesIn + InBufIdx;
  389.    PageTable[CurrentPage]^.PageSize := 0;
  390.    If CurrentPage > 1 then
  391.       PageTable[Pred(CurrentPage)]^.PageSize :=
  392.             PageTable[CurrentPage]^.PageOfs  -
  393.             PageTable[Pred(CurrentPage)]^.PageOfs;
  394. End {NewPage};
  395.  
  396. { --------------------------------------------------------------------------- }
  397.  
  398. Procedure Scan_Input;
  399. Var
  400.    I           : Word;
  401.    NewPageAddr : Pointer;     { Address of NewPage procedure              }
  402. Begin
  403.    Writeln;
  404.    Writeln('Scanning input file ... Please wait');
  405.    Reset(InFile, 1);
  406.    InFileSize := FileSize(InFile);
  407.    Read_Input;
  408.    PageCount   := 0; LineCount   := 0;
  409.    CurrentPage := 0; CurrentLine := 0; CurrentChar  := 0;
  410.    NewPageAddr := @NewPage;
  411.    NewPage;
  412.    While (Not EOF_InFile) do begin
  413.       Inline(
  414.         $8B/$0E/>BYTESIN/         {               mov      cx,[>BytesIn]        ; Loop thru all bytes read}
  415.         $8D/$36/>INBUF/           {               lea      si,[>InBuf]          ; ds:si points to the buffer}
  416.         $AC/                      {ScanLoop:      lodsb                         ; get one char from buffer}
  417.         $56/                      {               push     si                   ; need to keep track of index into}
  418.         $89/$F7/                  {               mov      di,si                ;    input buffer for file offset}
  419.         $8D/$36/>INBUF/           {               lea      si,[>InBuf]          ;    calculations in NewPage proc.}
  420.         $29/$F7/                  {               sub      di,si                ;    DI now has this index value.}
  421.         $89/$3E/>INBUFIDX/        {               mov      [>InBufIdx],di       ;    Store it for later use.}
  422.         $5E/                      {               pop      si                   ;    and restore our pointer}
  423.         $3C/$08/                  {               cmp      al,8                 ; a backspace?}
  424.         $74/$1A/                  {               jz       Backspace            ;    yup, go to it.}
  425.         $3C/$09/                  {               cmp      al,9                 ; a tabby?}
  426.         $74/$25/                  {               jz       HorizTab             ;    yup}
  427.         $3C/$0A/                  {               cmp      al,10                ; a linefeed?}
  428.         $74/$2D/                  {               jz       NewLine              ;    yup}
  429.         $3C/$0C/                  {               cmp      al,12                ; a formfeed?}
  430.         $74/$2F/                  {               jz       NewPage              ;    yup}
  431.         $3C/$0D/                  {               cmp      al,13                ; a carriage return?}
  432.         $74/$37/                  {               jz       CR                   ;    yup}
  433.         $3C/$20/                  {               cmp      al,' '               ; is it a "non-control" char?}
  434.         $72/$46/                  {               jb       GetNext              ;    no ... ignore it}
  435.         $FF/$06/>CURRENTCHAR/     {LegalChar:     inc word [>CurrentChar]       ; increment our character count}
  436.         $EB/$36/                  {               jmp short ChkLimits           ; and go check our counters}
  437.         $8B/$1E/>CURRENTCHAR/     {BackSpace:     mov      bx,[>CurrentChar]}
  438.         $83/$FB/$00/              {               cmp word bx,0                 ; do we have any other chars yet?}
  439.         $76/$37/                  {               jbe      GetNext              ;    no ... ignore it}
  440.         $FF/$0E/>CURRENTCHAR/     {               dec word [>CurrentChar]       ;   yes ... decrement char count}
  441.         $EB/$31/                  {               jmp short GetNext             ;   and go get next character}
  442.         $83/$06/>CURRENTCHAR/$08/ {HorizTab:      add word [>CurrentChar],8     ; advance counter to next}
  443.         $83/$26/>CURRENTCHAR/$F8/ {               and word [>CurrentChar],$FFF8 ;    tab stop}
  444.         $EB/$1B/                  {               jmp short ChkLimits           ;    and go check counter limits}
  445.         $FF/$06/>CURRENTLINE/     {NewLine:       inc word [>CurrentLine]       ; increment line counter,}
  446.         $EB/$15/                  {               jmp short ChkLimits           ;    and go check counter limits}
  447.         $50/                      {NewPage:       push     ax                   ; Ok, time to start a new page}
  448.         $51/                      {               push     cx                   ;    call the NewPage procedure}
  449.         $56/                      {               push     si}
  450.         $FF/$96/>NEWPAGEADDR/     {               Call     [bp+>NewPageAddr]    ;    settings}
  451.         $5E/                      {               pop      si}
  452.         $59/                      {               pop      cx}
  453.         $58/                      {               pop      ax}
  454.         $EB/$13/                  {               jmp short GetNext}
  455.         $BB/$00/$00/              {CR:            mov      bx,0}
  456.         $89/$1E/>CURRENTCHAR/     {               mov      [>CurrentChar],bx    ; adjust character counter}
  457.         $EB/$0A/                  {               jmp short GetNext}
  458.         $8B/$16/>CURRENTLINE/     {ChkLimits:     mov      dx,[>CurrentLine]}
  459.         $3B/$16/>LPP/             {               cmp      dx,[>LPP]}
  460.         $73/$E1/                  {               jae      NewPage}
  461.         $49/                      {GetNext:       dec      cx}
  462.         $E3/$02/                  {               jcxz     Done}
  463.         $EB/$8E);                 {               jmp      ScanLoop}
  464.                                   {Done:}
  465.       Read_Input;
  466.    end {While};
  467.  
  468.    NewPage;                               { Close out the current page }
  469.  
  470.    PageCount  := Pred(CurrentPage);
  471.    SheetCount := Round(PageCount / 2.0);
  472.  
  473.    Writeln;
  474.    Writeln('          Total lines to be printed: ', LineCount);
  475.    Writeln('          Total pages to be printed: ', PageCount);
  476.    Writeln('     Total sheets of paper required: ', SheetCount);
  477.    Writeln;
  478.  
  479. End {Scan_Input};
  480.  
  481. { --------------------------------------------------------------------------- }
  482.  
  483. Procedure Print(Msg : String);
  484. { Print a message on the printer                         }
  485. Begin
  486.    Inline(
  487.      $1E/                   {        push    ds              ; Save our data seg.}
  488.                             {;}
  489.                             {; Set the Critical Error Interrupt vector to}
  490.                             {; point back at the DOS handler whose address is}
  491.                             {; stored in the global system variable SaveInt24}
  492.                             {;}
  493.      $C5/$16/>SAVEINT24/    {        lds         dx,[>SaveInt24]}
  494.      $B4/$25/               {        mov         ah,$25}
  495.      $B0/$24/               {        mov         al,$24}
  496.      $CD/$21/               {        int         $21}
  497.                             {;}
  498.                             {; Ok, write the buffered data to the device}
  499.                             {;}
  500.      $1F/                   {        pop         ds                  ;Restore the "real" data seg}
  501.      $1E/                   {        push        ds                  ;and save back to the stack}
  502.      $8B/$1E/>HANDLE/       {        mov         bx,[>Handle]}
  503.      $8C/$D0/               {        mov         ax,ss}
  504.      $8E/$D8/               {        mov         ds,ax}
  505.      $8D/$96/>MSG/          {        lea         dx,[bp+>Msg]}
  506.      $42/                   {        inc         dx}
  507.      $31/$C9/               {        xor         cx,cx}
  508.      $8A/$8E/>MSG/          {        mov byte    cl,[bp+>Msg]}
  509.      $B4/$40/               {        mov         ah,$40}
  510.      $CD/$21/               {        int         $21}
  511.                             {;}
  512.                             {; Restore the Critical Error interrupt vector}
  513.                             {; to point back at Turbo Pascal's handler.}
  514.                             {; The address of Turbo's routine was previously}
  515.                             {; stored in the global pointer variable TurboInt24}
  516.      $C5/$16/>TURBOINT24/   {        lds         dx,[>TurboInt24]}
  517.      $B4/$25/               {        mov         ah,$25}
  518.      $B0/$24/               {        mov         al,$24}
  519.      $CD/$21/               {        int         $21}
  520.                             {;}
  521.      $1F);                  {        pop         ds          ; Restore the data seg.}
  522.    
  523. End {Print};
  524.  
  525. { --------------------------------------------------------------------------- }
  526.  
  527. Procedure Init_Page;
  528. Var
  529.    PgNo : String[4];
  530. Begin
  531.    If (CurrentPage AND $0001) <> 0 then         { if odd page number }
  532.       CurrentMar := BindMar
  533.    else
  534.       CurrentMar := 0;
  535.  
  536.    CurrentCPL := CPL - BindMar;
  537.  
  538.    GotoXY(MsgX, MsgY);
  539.    ClrEol;
  540.    Write(CurrentPage);
  541.  
  542.    If (CurrentPage > 2) And UseFF then
  543.       Print(FF);
  544.  
  545.    Print(Copies(' ', CurrentMar));
  546.  
  547.    If PrintPgNo then
  548.       PgNo := IntStr(CurrentPage, 0)
  549.    else
  550.       PgNo := '';
  551.  
  552.    If (CurrentPage AND $0001) <> 0 then begin   { if odd page number }
  553.       PageHdr := HdrName;
  554.       PageHdr := PageHdr +
  555.                  Copies(' ', ((CurrentCPL - Length(HdrDate)) DIV 2) - Length(PageHdr)) +
  556.                  HdrDate;
  557.       PageHdr := PageHdr +
  558.                  Copies(' ', CurrentCPL - Length(PageHdr) - Length(HdrPage) - Length(PgNo)) +
  559.                  HdrPage + PgNo;
  560.    end {then}
  561.    else begin
  562.       PageHdr := HdrPage + PgNo;
  563.       PageHdr := PageHdr +
  564.                  Copies(' ', ((CurrentCPL - Length(HdrDate)) DIV 2) - Length(PageHdr)) +
  565.                  HdrDate;
  566.       PageHdr := PageHdr +
  567.                  Copies(' ', CurrentCPL - Length(PageHdr) - Length(HdrName)) +
  568.                  HdrName;
  569.    end {if};
  570.  
  571.    If PrintHdr then begin
  572.       Print(PageHdr);
  573. (*
  574.       If you want your page header underlined, remove the comment
  575.       markers around the following statement.  Note that your printer
  576.       must be able to handle a standalone carriage return for this
  577.       to work properly (the printhead should move to the first print
  578.       position without advancing to the next printline).
  579.  
  580.       Print(CR + Copies(' ', CurrentMar) + Copies('_', Length(PageHdr)));
  581. *)
  582.  
  583.       Print(CR + LF + CR + LF);
  584.       Print(Copies(' ', CurrentMar));
  585.       CurrentLine := 2;
  586.    end {then}
  587.    else
  588.       CurrentLine := 0;
  589.  
  590.    CurrentChar := 0;
  591.  
  592. End {Init_Page};
  593.  
  594. { --------------------------------------------------------------------------- }
  595.  
  596. Procedure PrintPage;
  597. Var
  598.    PrintAddr : Pointer;
  599.    ReadAddr  : Pointer;
  600.    LMar      : String;
  601.    PrtString : String;
  602. Begin
  603.    Seek(InFile, PageTable[CurrentPage]^.PageOfs);
  604.    Read_Input;
  605.    Init_Page;    { initialize Line counter & optionally print page headers }
  606.    PrintAddr := @Print;
  607.    ReadAddr  := @Read_Input;
  608.    LMar      := Copies(' ', CurrentMar);
  609.    Inline(
  610.                                    {; Set the Critical Interrupt vector to point back}
  611.                                    {; at the DOS handler whose address is stored}
  612.                                    {; in the global system variable SaveInt24}
  613.      $1E/                          {            push        ds}
  614.      $C5/$16/>SAVEINT24/           {            lds         dx,[>SaveInt24]}
  615.      $B4/$25/                      {            mov         ah,$25}
  616.      $B0/$24/                      {            mov         al,$24}
  617.      $CD/$21/                      {            int         $21}
  618.      $1F/                          {            pop         ds}
  619.                                    {;}
  620.      $E9/$1F/$00/                  {SkipProcs:  jmp         EntryPt}
  621.                                    {;}
  622.                                    {;}
  623.                                    {BuffCheck:}
  624.                                    {;}
  625.                                    {;   On entry to this subroutine, it is assumed that DS:SI is pointing to}
  626.                                    {;   somewhere inside the file input buffer.}
  627.                                    {;}
  628.      $56/                          {            push        si                  ;save si}
  629.      $57/                          {            push        di                  ;save di}
  630.      $89/$F7/                      {            mov         di,si}
  631.      $8D/$36/>INBUF/               {            lea         si,[>InBuf]}
  632.      $29/$F7/                      {            sub         di,si}
  633.                                    {;}
  634.                                    {;   the next two statements will need a manual fix-up after being}
  635.                                    {;   assembled with Baldwin's Inline program (version 2.19 - 4/26/88).}
  636.                                    {;   Dave's assembler will generate the following code:}
  637.                                    {;            $90/}
  638.                                    {;            $83/$FF/<BufSize}
  639.                                    {;   patch the resulting OBJ file by removing the NOP instruction}
  640.                                    {;   (ie. erase the $90/) and change the compare instruction to:}
  641.                                    {;            $81/$FF/>BufSize}
  642.                                    {;}
  643.                                    {            nop}
  644.      $81/$FF/>BUFSIZE/             {            cmp word    di,>BufSize       ;time to read more data?}
  645.      $5F/                          {            pop         di}
  646.      $5E/                          {            pop         si}
  647.      $76/$0C/                      {            jbe         NoRead              ; ... not yet}
  648.      $51/                          {            push        cx                  ;save loop counter}
  649.      $57/                          {            push        di}
  650.      $FF/$96/>READADDR/            {            call        [bp+>ReadAddr]      ;read data into buffer}
  651.      $5F/                          {            pop         di}
  652.      $59/                          {            pop         cx                  ;restore loop counter}
  653.      $8D/$36/>INBUF/               {            lea         si,[>InBuf]         ;DS:SI points to input buffer}
  654.      $C3/                          {NoRead:     ret                             ;Return to caller}
  655.                                    {;}
  656.                                    {;}
  657.      $8D/$36/>PAGETABLE/           {EntryPt:    lea         si,[>PageTable]}
  658.      $A1/>CURRENTPAGE/             {            mov         ax,[>CurrentPage]}
  659.      $48/                          {            dec         ax}
  660.      $D1/$E0/                      {            shl         ax,1}
  661.      $D1/$E0/                      {            shl         ax,1}
  662.      $01/$C6/                      {            add         si,ax}
  663.      $C4/$3C/                      {            les         di,[si]             ;ES:DI points to pagetable entry}
  664.      $26/$8B/$4D/$04/              {        es: mov         cx,[di+4]           ;CX now has pagesize in bytes}
  665.      $8D/$36/>INBUF/               {            lea         si,[>InBuf]         ;DS:SI points to input buffer}
  666.      $8C/$D0/                      {            mov         ax,ss}
  667.      $8E/$C0/                      {            mov         es,ax}
  668.      $8D/$BE/>PRTSTRING/           {            lea         di,[bp+>PrtString]  ;ES:DI --> String to be built & printed}
  669.      $B0/$00/                      {            mov         al,0}
  670.      $AA/                          {            stosb                           ;store 0 in the length byte}
  671.                                    {;}
  672.      $E8/$BB/$FF/                  {Looper:     call        BuffCheck           ;make sure buffer isn't empty}
  673.      $AC/                          {            lodsb                           ;get a byte from the buffer}
  674.      $3C/$08/                      {            cmp         al,8                ;backspace?}
  675.      $75/$16/                      {            jnz         LineFeed            ;no, go check for other char types}
  676.      $83/$3E/>CURRENTCHAR/$00/     {            cmp word    [>CurrentChar],0    ;anything to backspace over?}
  677.      $75/$03/                      {            jnz         Backup              ;yes, process the character}
  678.      $E9/$E0/$00/                  {            jmp         GetNext             ;no, skip it and get next char}
  679.      $FF/$0E/>CURRENTCHAR/         {Backup:     dec word    [>CurrentChar]      ;decrement position counter}
  680.      $AA/                          {            stosb                           ;store the BS in the print string}
  681.      $FE/$86/>PRTSTRING/           {            inc byte    [bp+>PrtString]     ;and bump the length byte}
  682.      $E9/$B0/$00/                  {            jmp         ChkString           ;go see if time to print the string}
  683.                                    {;}
  684.      $3C/$0A/                      {LineFeed:   cmp         al,10               ;linefeed?}
  685.      $75/$0C/                      {            jnz         CR                  ;no, go check for other char types}
  686.      $FF/$06/>CURRENTLINE/         {            inc word    [>CurrentLine]}
  687.      $AA/                          {            stosb                           ;store LF in print string}
  688.      $FE/$86/>PRTSTRING/           {            inc byte    [bp+>PrtString]     ;bump string's length byte}
  689.      $E9/$A0/$00/                  {            jmp         ChkString           ;go see if time to print the string}
  690.                                    {;}
  691.      $3C/$0D/                      {CR:         cmp         al,13               ;carriage return?}
  692.      $75/$45/                      {            jnz         HTab                ;no, go check for other char types}
  693.      $AA/                          {            stosb                           ;store CR in print string}
  694.      $FE/$86/>PRTSTRING/           {            inc byte    [bp+>PrtString]     ;bump string length byte}
  695.      $C7/$06/>CURRENTCHAR/$00/$00/ {            mov word    [>CurrentChar],0    ;reset current character ptr to 0}
  696.      $83/$F9/$01/                  {            cmp         cx,1                ;Are we at end-of-page yet?}
  697.      $77/$03/                      {            ja          ChkLF               ;no, see if next char is linefeed}
  698.      $E9/$89/$00/                  {            jmp         ChkString           ;Yes, skip the following code}
  699.      $E8/$76/$FF/                  {ChkLF:      call        BuffCheck           ;make sure buffer isn't empty}
  700.      $80/$3C/$0A/                  {            cmp byte    [si],10             ;is next char a linefeed?}
  701.      $75/$0F/                      {            jnz         NoLF                ;no, skip this part}
  702.      $A4/                          {            movsb                           ;yes, put it into prtstring before margin}
  703.      $49/                          {            dec         cx}
  704.      $FE/$86/>PRTSTRING/           {            inc byte    [bp+>PrtString]     ;bump length byte}
  705.      $FF/$06/>CURRENTLINE/         {            inc word    [>CurrentLine]      ;increment current line counter}
  706.      $83/$F9/$01/                  {            cmp         cx,1}
  707.      $76/$72/                      {            jbe         ChkString           ;if we're done with page ...}
  708.      $51/                          {NoLF:       push        cx                  ;   otherwise, print a margin}
  709.      $1E/                          {            push        ds}
  710.      $56/                          {            push        si                  ;save our buffer pointer}
  711.      $8C/$D0/                      {            mov         ax,ss}
  712.      $8E/$D8/                      {            mov         ds,ax}
  713.      $8D/$B6/>LMAR/                {            lea         si,[bp+>LMar]       ;get ready to copy left margin}
  714.      $AC/                          {            lodsb                           ; into print string}
  715.      $98/                          {            cbw}
  716.      $89/$C1/                      {            mov         cx,ax               ;byte count into cl}
  717.      $00/$8E/>PRTSTRING/           {            add         [bp+>PrtString],cl  ;add it to existing string length}
  718.      $F2/$A4/                      {        rep movsb                           ;and append margin to print string}
  719.      $5E/                          {            pop         si}
  720.      $1F/                          {            pop         ds}
  721.      $59/                          {            pop         cx}
  722.      $E9/$57/$00/                  {            jmp         ChkString}
  723.                                    {;}
  724.      $3C/$09/                      {HTab:       cmp         al,09               ;a tabby?}
  725.      $75/$33/                      {            jnz         CtrlChar            ;no, go check for other char types}
  726.      $8B/$1E/>CURRENTCHAR/         {            mov         bx,[>CurrentChar]   ;get current line position}
  727.      $83/$C3/$08/                  {            add         bx,8                ;determine next tab stop}
  728.      $83/$E3/$F8/                  {            and         bx,$FFF8}
  729.      $3B/$1E/>CURRENTCPL/          {            cmp         bx,[>CurrentCPL]    ;past end of line yet?}
  730.      $76/$0B/                      {            jbe         SameLine            ;not yet ...}
  731.      $8B/$16/>CURRENTCPL/          {            mov         dx,[>CurrentCPL]}
  732.      $89/$16/>CURRENTCHAR/         {            mov         [>CurrentChar],dx   ;CurrentChar := CurrentCPL}
  733.      $E9/$5C/$00/                  {            jmp         GetNext}
  734.      $53/                          {SameLine:   push        bx}
  735.      $2B/$1E/>CURRENTCHAR/         {            sub         bx,[>CurrentChar]   ;bx = no. of spaces to add to print line}
  736.      $51/                          {            push        cx}
  737.      $89/$D9/                      {            mov         cx,bx}
  738.      $B0/$20/                      {            mov         al,' '}
  739.      $00/$8E/>PRTSTRING/           {            add         [bp+>PrtString],cl}
  740.      $F2/$AA/                      {        rep stosb                           ;space out to tab stop}
  741.      $59/                          {            pop         cx}
  742.      $5B/                          {            pop         bx}
  743.      $89/$1E/>CURRENTCHAR/         {            mov         [>CurrentChar],bx}
  744.      $EB/$20/                      {            jmp short   ChkString           ;go see if string is ready for printer}
  745.                                    {;}
  746.      $3C/$20/                      {CtrlChar:   cmp         al,' '              ;char < ascii 32?}
  747.      $73/$07/                      {            jae         TxtChar             ;no, must be printable ...}
  748.      $AA/                          {            stosb                           ;add unprintable char to string}
  749.      $FE/$86/>PRTSTRING/           {            inc byte    [bp+>PrtString]}
  750.      $EB/$39/                      {            jmp short   GetNext             ;  but don't bump position counter}
  751.                                    {;}
  752.      $8B/$1E/>CURRENTCHAR/         {TxtChar:    mov         bx,[>CurrentChar]   ;get character position within line}
  753.      $3B/$1E/>CURRENTCPL/          {            cmp         bx,[>CurrentCPL]    ;are we at end of line yet?}
  754.      $73/$2F/                      {            jae         GetNext             ;yes, skip this character}
  755.      $AA/                          {            stosb                           ;no, store char in string}
  756.      $FE/$86/>PRTSTRING/           {            inc byte    [bp+>PrtString]}
  757.      $FF/$06/>CURRENTCHAR/         {            inc word    [>CurrentChar]      ;bump position counter}
  758.      $EB/$00/                      {            jmp short   ChkString           ;and go check length of print string}
  759.                                    {;}
  760.      $80/$BE/>PRTSTRING/$80/       {ChkString:  cmp byte    [bp+>PrtString],128 ;only let it grow to 128 chars or so}
  761.      $72/$1D/                      {            jb          GetNext}
  762.      $51/                          {PrintIt:    push        cx                  ; Save everything just in case}
  763.      $56/                          {            push        si}
  764.      $57/                          {            push        di}
  765.      $1E/                          {            push        ds}
  766.      $8D/$BE/>PRTSTRING/           {            lea         di,[bp+>PrtString]}
  767.      $16/                          {            push        ss}
  768.      $57/                          {            push        di}
  769.      $FF/$96/>PRINTADDR/           {            call        [bp+>PrintAddr]     ;print the string}
  770.      $1F/                          {            pop         ds                  ;Restore all saved regs}
  771.      $5F/                          {            pop         di}
  772.      $5E/                          {            pop         si}
  773.      $59/                          {            pop         cx}
  774.      $8C/$D0/                      {            mov         ax,ss               ;reset our pointer to the string}
  775.      $8E/$C0/                      {            mov         es,ax               ;  variable that holds the chars}
  776.      $8D/$BE/>PRTSTRING/           {            lea         di,[bp+>PrtString]  ;  to be printed}
  777.      $B0/$00/                      {            mov         al,0                ;stuff 0 in the length byte}
  778.      $AA/                          {            stosb}
  779.                                    {;}
  780.      $49/                          {GetNext:    dec         cx                  ;decrement our loop counter}
  781.      $E3/$03/                      {            jcxz        Done                ;done yet?}
  782.      $E9/$08/$FF/                  {            jmp         Looper              ;no, go back for more}
  783.                                    {;}
  784.      $80/$BE/>PRTSTRING/$00/       {Done:       cmp byte    [bp+>PrtString],0   ;any leftovers?}
  785.      $74/$0A/                      {            jz          TimeToExit          ;no, exit}
  786.      $8D/$BE/>PRTSTRING/           {            lea         di,[bp+>PrtString]}
  787.      $16/                          {            push        ss}
  788.      $57/                          {            push        di}
  789.      $FF/$96/>PRINTADDR/           {            call        [bp+>PrintAddr]     ;print the string}
  790.                                    {;}
  791.                                    {TimeToExit:}
  792.                                    {; Restore the Critical Error interrupt vector}
  793.                                    {; to point back at Turbo Pascal's handler.}
  794.                                    {; The address of Turbo's routine was previously}
  795.                                    {; stored in the global pointer variable TurboInt24}
  796.      $1E/                          {            push        ds}
  797.      $C5/$16/>TURBOINT24/          {            lds         dx,[>TurboInt24]}
  798.      $B4/$25/                      {            mov         ah,$25}
  799.      $B0/$24/                      {            mov         al,$24}
  800.      $CD/$21/                      {            int         $21}
  801.      $1F);                         {            pop         ds}
  802.    
  803. End {PrintPage};
  804.  
  805. { --------------------------------------------------------------------------- }
  806.  
  807. Procedure Print_Odd_Pages;
  808. Var
  809.    LastPage    : Word;
  810.    I           : Word;
  811. Begin
  812.  
  813.    If (PageCount AND $0001) = 0 then
  814.       LastPage := Pred(PageCount)
  815.    else
  816.       LastPage := PageCount;
  817.  
  818.    CurrentPage := 1;
  819.  
  820.    While CurrentPage <= LastPage do begin
  821.       PrintPage;
  822.       Inc(CurrentPage, 2);
  823.    end {while};
  824.  
  825.    If CurrentLine <= LPP then
  826.       If UseFF then
  827.          Print(FF)
  828.       else
  829.          For I := CurrentLine + 1 To LPP do
  830.             Print(CR + LF);
  831.  
  832. End {Print_Odd_Pages};
  833.  
  834. { --------------------------------------------------------------------------- }
  835.  
  836. Procedure Print_Even_Pages;
  837. Var
  838.    LastPage    : Word;
  839.    I           : Word;
  840. Begin
  841.  
  842.    If (PageCount AND $0001) = 1 then
  843.       LastPage := Pred(PageCount)
  844.    else
  845.       LastPage := PageCount;
  846.  
  847.    CurrentPage := 2;
  848.  
  849.    While CurrentPage <= LastPage do begin
  850.       PrintPage;
  851.       Inc(CurrentPage, 2);
  852.    end {while};
  853.  
  854.    If CurrentLine <= LPP then
  855.       If UseFF then
  856.          Print(FF)
  857.       else
  858.          For I := CurrentLine + 1 To LPP do
  859.             Print(CR + LF);
  860.  
  861.    If (PageCount AND $0001) = 1 then
  862.       If UseFF then
  863.          Print(FF)
  864.       else
  865.          For I := 1 to LPP do
  866.             Print(CR + LF);
  867.  
  868. End {Print_Even_Pages};
  869.  
  870. { --------------------------------------------------------------------------- }
  871.  
  872. Var
  873.    Ch : Char;
  874. Begin
  875.    Parse_Command_Line;
  876.    Prog_Init;
  877.    Scan_Input;
  878.    If OddFirst or (PageCount = 1) then
  879.       Write('Ready to print the odd pages ... Press any key to start')
  880.    else
  881.       Write('Ready to print the even pages ... Press any key to start');
  882.    Ch := Readkey;
  883.    Writeln;
  884.    Writeln;
  885.    Write('Printing page number ');
  886.    MsgX := WhereX; MsgY := WhereY;
  887.    If OddFirst or ((Not OddFirst) and (PageCount = 1)) then
  888.       Print_Odd_Pages
  889.    else
  890.       Print_Even_Pages;
  891.  
  892.    If PageCount > 1 then begin
  893.       Writeln;
  894.       Writeln;
  895.       Writeln('Ready to print the remaining pages ...');
  896.       Write('Turn the paper over and press any key when ready');
  897.       Ch := ReadKey;
  898.       Writeln;
  899.       Writeln;
  900.       Write('Printing page number ');
  901.       MsgX := WhereX; MsgY := WhereY;
  902.       If OddFirst then
  903.          Print_Even_Pages
  904.       else
  905.          Print_Odd_Pages;
  906.    end {if};
  907.  
  908.    Close(InFile);
  909.    If OutFileName <> '' then
  910.       Close(OutFile);
  911.  
  912.    Writeln;
  913.    Writeln;
  914.    Writeln('Done.');
  915.    Writeln;
  916. End.
  917.